package com.flute.haflute.web;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;

import org.mortbay.jetty.Connector;
import org.mortbay.jetty.Handler;
import org.mortbay.jetty.NCSARequestLog;
import org.mortbay.jetty.RequestLog;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.bio.SocketConnector;
import org.mortbay.jetty.handler.DefaultHandler;
import org.mortbay.jetty.handler.HandlerCollection;
import org.mortbay.jetty.handler.RequestLogHandler;
import org.mortbay.jetty.security.HashUserRealm;
import org.mortbay.jetty.webapp.WebAppContext;

/**
 * 包装嵌入的Jetty server.
 * 
 * 启动所有的Webapps下面的项目
 * 
 * @author Jazz
 * 
 */
public class SimpleHttpServer {

	private int port;
	private Server server = null;

	/**
	 * 默认的端口.
	 */
	public static final int DEFAULT_PORT = 8080;

	/**
	 * Webapp的context.
	 */
	private List<WebAppContext> contexts = new ArrayList<WebAppContext>();

	/**
	 * root webapp的名字
	 */
	private static final String ROOT_WEBAPP = "root";

	/**
	 * admin webapp的名字.
	 */
	private static final String ADMIN_WEBAPP = "admin";

	/**
	 * stderr/stdout 日志文件.
	 */
	public static String DEFAULT_OUT = "logs/jetty.log";

	/**
	 * 配置在webapps下的项目列表.
	 */
	private static final List<String> webapps = Arrays.asList(new String[] {
			ROOT_WEBAPP, ADMIN_WEBAPP });

	public SimpleHttpServer() throws Exception {
		this(DEFAULT_PORT, true);
	}

	public SimpleHttpServer(int port, boolean expandWebapps) throws Exception {
		this(SimpleHttpServer.webapps, port, expandWebapps);
	}

	public SimpleHttpServer(String name, String context,
			Collection<String> hosts, int port, boolean expandWebapps)
			throws Exception {
		initialize(hosts, port);
		addWebapp(name, context, expandWebapps);
		RequestLogHandler rlh = new RequestLogHandler();
		rlh.setRequestLog(getServerLogging());
		this.server.addHandler(rlh);
	}

	public SimpleHttpServer(List<String> webapps, int port,
			boolean expandWebapps) throws Exception {
		initialize(new ArrayList<String>(), port);
		for (Iterator<String> i = webapps.iterator(); i.hasNext();) {
			addWebapp((String) i.next(), null, expandWebapps);
		}
		RequestLogHandler rlh = new RequestLogHandler();
		rlh.setRequestLog(getServerLogging());
		this.server.addHandler(rlh);
	}

	/**
	 * 添加一个webapp.
	 * 
	 * @param name
	 *            加入WebApp的名字.
	 * @param context
	 *            加入WebApp的Context.
	 * @param expand
	 *            是否扩展WebApps.
	 * @throws IOException
	 */
	protected void addWebapp(String name, String context, boolean expand)
			throws IOException {
		File ptr = new File(getWARSPath(), name + ".war");
		if (!ptr.exists()) {
			ptr = new File(getWARSPath(), name);
			if (!ptr.exists()) {
				throw new FileNotFoundException(ptr.getAbsolutePath());
			}
		}
		if (context == null || context.length() <= 0) {
			context = "/" + ((name.equals(ROOT_WEBAPP)) ? "" : name);
		}
		WebAppContext c = new WebAppContext(context, ptr.getAbsolutePath());
		if (context.equals("/")) {
			c.setWar(name);
		}

		c.setExtractWAR(expand);
		c.getSessionHandler().getSessionManager().setMaxInactiveInterval(86400);
		contexts.add(c);

		HandlerCollection handlers = new HandlerCollection();
		handlers.setHandlers(new Handler[] { c, new DefaultHandler() });
		server.setHandler(handlers);
	}

	/**
	 * 初始化服务器.
	 * 
	 * @param hosts
	 *            绑定的主机名; 如果empty或者null, 将绑定所有的接口
	 * @param port
	 *            监听的端口
	 */
	protected void initialize(Collection<String> hosts, int port) {
		this.server = new Server();
		this.port = port;
		if (hosts.isEmpty()) {
			Connector listener = new SocketConnector();
			listener.setPort(port);
			server.addConnector(listener);
			return;
		}

		for (String host : hosts) {
			Connector listener = new SocketConnector();
			listener.setHost(host);
			listener.setPort(port);
			server.addConnector(listener);
		}
	}

	protected static Collection<String> determineHosts(boolean lho) {
		Collection<String> hosts = new ArrayList<String>();
		if (lho) {
			hosts.add("127.0.0.1");
		}
		return hosts;
	}

	/**
	 * 建立日志文件.
	 * 
	 * @return 加入RequestLog实例到一个服务器.
	 * @throws Exception
	 */
	protected RequestLog getServerLogging() throws Exception {
		File file = new File(DEFAULT_OUT);
		file = file.getCanonicalFile();
		File parent = file.getParentFile();
		if (!parent.exists() && parent.isDirectory()) {
			if (!parent.mkdirs()) {
				System.err.println("can not Make workdir:" + parent);
			}
		}
		NCSARequestLog a = new NCSARequestLog(DEFAULT_OUT);
		a.setRetainDays(90);
		a.setAppend(true);
		a.setExtended(false);
		a.setLogTimeZone("GMT");
		a.start();
		return a;
	}

	public static File getWarsdir() throws IOException {
		return getSubDir("webapps");
	}

	private static String getWARSPath() throws IOException {
		String webappsPath = getWarsdir().getAbsolutePath();
		if (!webappsPath.endsWith(File.separator)) {
			webappsPath = webappsPath + File.separator;
		}
		return webappsPath;
	}

	protected static File getSubDir(String subdirName, boolean fail)
			throws IOException {
		File dir = new File(SimpleHttpServer.class.getResource("/").getPath(),
				subdirName);
		if (!dir.exists()) {
			if (fail) {
				throw new IOException("Cannot find subdir: " + dir.getPath());
			}
			dir = null;
		}
		return dir;
	}

	protected static File getSubDir(String subdirName) throws IOException {
		return getSubDir(subdirName, true);
	}

	protected static boolean isDevelopment() {
		return true;
	}

	/**
	 * 启动服务器.
	 * 
	 * @throws Exception
	 */
	public synchronized void startServer() throws Exception {

		this.server.start();
	}

	/**
	 * 停止运行中的服务器.
	 * 
	 * @throws InterruptedException
	 */
	public synchronized void stopServer() throws InterruptedException {

		if (server != null) {
			try {
				server.stop();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	protected void finalize() throws Throwable {

		stopServer();
		super.finalize();
	}

	public int getPort() {

		return this.port;
	}

	public Server getServer() {

		return server;
	}

	private WebAppContext getContext(String contextName) {

		WebAppContext context = null;

		if (contexts == null) {
			throw new NullPointerException("No contexts available.");
		}

		if (!contextName.startsWith("/")) {
			contextName = '/' + contextName;
		}
		for (Iterator<WebAppContext> i = contexts.iterator(); i.hasNext();) {
			WebAppContext c = (WebAppContext) i.next();
			if (c.getWar().equals(contextName)) {
				context = c;
				break;
			}
		}

		if (context == null) {
			throw new NoSuchElementException("Unknown webapp: " + contextName);
		}

		return context;
	}

	/**
	 * 设置服务器上面的Realm，再添加密码 该配置在web.xml中
	 * 
	 * @param realmName
	 *            配置的Realm的名字.
	 * @param contextName
	 *            使用realm的上下文名字.
	 * @param authProperties
	 *            登录的名字和密码
	 * @return 返回Realm的Hash
	 * 
	 * @throws IOException
	 */
	public HashUserRealm setAuthentication(String realmName,
			String contextName, String authProperties) throws IOException {
		HashUserRealm realm = (authProperties != null && authProperties
				.length() > 0) ? new HashUserRealm(realmName, authProperties)
				: new HashUserRealm(realmName);
		server.addUserRealm(realm);
		if (contextName == null || contextName.length() <= 0) {
			contextName = realmName;
		}
		WebAppContext context = getContext(contextName);
		context.getSecurityHandler().setUserRealm(realm);
		return realm;
	}

	public void setAuthentication(String realmName, String contextName,
			String username, String password, String role) throws IOException {
		HashUserRealm realm = setAuthentication(realmName, contextName, null);
		realm.put(username, password);
		realm.addUserToRole(username, role);
	}

	/**
	 * 重新设置管理员的登录信息.
	 * 
	 * @param realmAndRoleName
	 *            realm和Role的名字，这里总是admin
	 * @param oldUsername
	 *            之前的用户名
	 * @param newUsername
	 *            新的用户名
	 * @param newPassword
	 *            新的密码
	 */
	public void resetAuthentication(String realmAndRoleName,
			String oldUsername, String newUsername, String newPassword) {
		HashUserRealm[] realms = (HashUserRealm[]) server.getUserRealms();
		for (HashUserRealm realm : realms) {
			if (realm.getName().equals(realmAndRoleName)) {
				// realm.remove(oldUsername);

				realm.put(newUsername, newPassword);
				realm.addUserToRole(newUsername, realmAndRoleName);
				break;
			}
		}

	}

	/**
	 * @return Returns Root Webapp的名字.
	 */
	public static String getRootWebappName() {
		return ROOT_WEBAPP;
	}

	/**
	 * Returns 服务器监听的hosts列表.
	 * 
	 * @return 服务器监听的hosts列表.
	 */
	public Collection<String> getHosts() {
		ArrayList<String> result = new ArrayList<String>();
		for (Connector listener : server.getConnectors()) {
			result.add(listener.getHost());
		}
		return result;
	}

	public void addWebAppContext(String context, String webAppPath)
			throws IOException {
		WebAppContext logContext = new WebAppContext(context, webAppPath);

		logContext.getSessionHandler().getSessionManager()
				.setMaxInactiveInterval(86400);
		contexts.add(logContext);

		HandlerCollection handlers = new HandlerCollection();
		handlers.setHandlers(new Handler[] { logContext, new DefaultHandler() });
		server.setHandler(handlers);
	}
}
